package services

import (
	"errors"
	"fmt"
	global2 "longmen/server/config/global"
	"longmen/server/pkg/common/api_models"
	"longmen/server/pkg/common/helper/const"
	"longmen/server/pkg/common/helper/util"
	"longmen/server/pkg/db/mysql"
	"longmen/server/pkg/db/mysql/db_live"
	"longmen/server/pkg/db/mysql/db_main"
	"longmen/server/pkg/db/mysql/models/models_live"
	"longmen/server/pkg/db/mysql/models/models_main"
	"longmen/server/pkg/db/redis/cache"
	cache2 "longmen/server/pkg/db/redis/cache"
	"strings"
	"time"

	"github.com/google/uuid"
	"github.com/streadway/amqp"
)

// 发送礼物
func SendGift(r *api_models.SendGiftReq, uid int) (*api_models.SendGiftResp, error) {
	global2.Log.Infof("6SendGift9:%v", r)
	_, err := SendGiftCheck(r, uid)
	if err != nil {
		return nil, err
	}
	gift, err := cache.GetGiftInfo(r.Giftid)
	if err != nil {
		return nil, err
	}
	liveInfo, _ := db_live.GetLiveParam(map[string]interface{}{"anchorid": r.Anchorid})
	if liveInfo.Liveid == 0 {
		global2.Log.Errorf("找不到主播的直播间")
		liveInfo.Liveid = r.Liveid
	}
	//活动道具只能从背包扣
	if gift.Type == _const.ActivityGiftType {
		//扣减礼物
		if !db_main.UseProps(global2.GetX(), uid, r.Giftid, r.Type, r.Count) {
			return nil, errors.New("您暂未获得该礼物,或礼物数量不够，如有疑问请联系客服QQ或邮箱")
		}
	}
	//计费
	//查询出折扣 计算折扣
	userDiscount := db_main.GetUserDiscount(uid)
	giftPrint := gift.Price * r.Count
	original := giftPrint
	remark := ""
	if gift.Type != _const.ActivityGiftType {
		giftPrint = giftPrint * userDiscount.GiftDiscount / 100
		if len(userDiscount.Name) > 0 {
			remark = fmt.Sprintf("%s%s", userDiscount.Name, global2.Lang.T("discount"))
		}
	}
	afterAmount := 0
	anchor, err := mysql.GetUserParam(map[string]interface{}{"id": r.Anchorid})
	if err != nil {
		return nil, err
	}
	iSharingRatio := anchor.SharingRatio
	if iSharingRatio == 0 {
		//global2.Log.Warnf("6SendGift9主播分成设置有错，订单设为最低的1%")
		iSharingRatio = 1
	}
	anchorDiamondGet := giftPrint * iSharingRatio / 100 //假设是鹰钻，那得到的就是这个值

	var fundsType _const.FundsType
	//活动礼物不扣费，计价单位不能是diamond，不参与分成；但是有记录，有给主播加钱
	if gift.Type != _const.ActivityGiftType {
		if gift.PriceType == int(_const.CoinDiamond) {
			fundsType = _const.CoinDiamond

		} else if gift.PriceType == int(_const.CoinGold) {
			fundsType = _const.CoinGold
		}
		//如果计价单位是gold，那么送记人有扣钱，收礼人有加钱，有礼物记录；但是不会有公会分成
		if fundsType == _const.CoinDiamond { //anchor.ProfitDiamond > 0 &&
			// 如果主播有工会经纪人
			if anchor.GuildManagerId > 0 {
				//首先只要写入主播赚多少钱就行啦，要把主播上级上上级...分成方案记入到缓存中
				guildRate, plan, err := cache.GetAnchorSharePlan(anchor.Id)
				if err != nil {
					global2.Log.Warnf("6SendGift9主播公会分成配置出错, 按无公会处理%v", err)
				} else {
					anchorDiamondGet, _ = util.CalcCommissionListByAmount(guildRate, plan, giftPrint)
				}
				if anchorDiamondGet == 0 {
					global2.Log.Infof("6SendGift9主播%d分成得0，公会分成金额设置有错，给主播自己分成", anchor.Id)
					anchorDiamondGet = giftPrint * iSharingRatio / 100
				} //end if主播0分成
			} //end if 有公会
		} //end if 是克拉
	} else { //else:是活动礼物
		giftPrint = 0
	} //end if活动礼物
	//global2.Log.Debugf("6SendGift9主播分成：%d/100,礼物价值:%d", iSharingRatio, giftPrint)
	//生成礼物日志
	giftLog := &models_main.GiftLog{
		Giftid: r.Giftid, Anchorid: anchor.Id, Uid: uid,
		Liveid: liveInfo.Liveid, Count: r.Count,
		Spend: original, SpendType: gift.PriceType, CreateTime: time.Now(),
		AnchorProfit: anchorDiamondGet, ActualSpend: giftPrint, Remark: remark}
	//记送礼物记录，扣钱加钱一条龙服务（事务）
	//fixme 这里在写db_gift_log  写入主播收尹
	giftLogId, err := db_main.ProcessSendGift(giftLog)
	if err != nil { //主记录失败的话整个去球，不接着写缓存
		global2.Log.Errorf("6SendGift9写库failed%v", err)
		return nil, err
	}
	err = cache2.PushFundFlow(
		anchor.Id, anchorDiamondGet, fundsType, _const.PROFIT_COMMISSION_ANCHOR)
	if err != nil {
		global2.Log.Warnf("6SendGift9流水进ntas队列failed%v", err)
	}
	if giftPrint != 0 { //活动礼物送礼人不扣款
		err = cache2.PushFundFlow(
			uid, giftPrint, fundsType, _const.CONSUME_GIFT)
		if err != nil {
			global2.Log.Warnf("6SendGift9流水进ntas队列failed%v", err)
		}
	}
	giftLog.Id = giftLogId
	//先写入nats 从NATS 消费并写写入交易记录db_user_profit 表中
	//fixme 写入主播收收尹与记录
	//err = cache.PushGiftLog(giftLog)
	//if err != nil {
	//	global.Log.Errorf("6SendGift9GiftLog进nats队列failed%v", err)
	//}
	err = cache.OnTaskEvent(giftLog.Uid, _const.TASK_NEWBIE_N_GIFT, 1) //送了一个礼
	if err != nil {
		global2.Log.Warnf("任务出错%v", err)
	}
	return &api_models.SendGiftResp{Gold: 0, Diamond: afterAmount}, nil
}

// 发送礼物，条件检测
func SendGiftCheck(r *api_models.SendGiftReq, uid int) (*api_models.SendGiftResp, error) {
	//check liveid 这个需要请求方添加
	/*if int(r.Liveid) == 0 {
		return nil, errors.New("直播间liveid不对")
	}*/
	if r.Anchorid == uid {
		return nil, errors.New("无法给自己赠送礼物")
	}
	if r.Count < 1 { //range
		return nil, errors.New("礼物数量不能小于1")
	}
	/*anchorMap := cache.GetAnchorIds()
	  if _, ok := anchorMap[cast.ToString(r.Anchorid)]; !ok {
	  	global.Log.Infof("6SendGift9主播不合法，主播map:%v", anchorMap)
	  	return nil, errors.New("主播不合法")
	  }/*去掉验证，因为tencent_im后，开播先是要合法主播的*/
	gift, err := cache.GetGiftInfo(r.Giftid)
	if err != nil {
		return nil, err
	}
	//活动道具只能从背包扣
	if gift.Type == _const.ActivityGiftType {
		//检查背包里是未有使用礼物是否足够

		counts := db_main.CountBackpackParam(uid, r.Giftid)
		if counts < int64(r.Count) {
			return nil, errors.New("背包里礼物数量不够,如有疑问请联系客服QQ或邮箱")
		}
	}
	//fixme 也许这里不是不能大于， 先这样写
	if gift.Type == _const.GIFTTYPE_LUXURY && r.Count > 1 {
		global2.Log.Infof("6GIFTTYPE_LUXURY 赠送豪华礼物数量不能大于1:%d 数量:%d", gift.Type, r.Count)
		return nil, errors.New("豪华礼物数量不能大于1")
	}
	if !util.ValidRange(_const.VALUE_GIFT_PRICE, float64(gift.Price)) {
		return nil, errors.New("礼物价格错误")
	}
	//费用计算
	giftPrint := gift.Price * r.Count

	diamondBalance, goldBalance, err := db_main.GetBalance(uid)
	if err != nil {
		return nil, errors.New("获取余额超时")
	}
	global2.Log.Infof("6SendGift9%v, balance:%d,%d", r, diamondBalance, goldBalance)
	//活动礼物不计算费用
	if gift.Type != _const.ActivityGiftType {
		//计算折扣
		userDiscount := db_main.GetUserDiscount(uid)
		giftPrint = giftPrint * userDiscount.GiftDiscount / 100
		//fixme 直接扣款, 扣款成功并查询余额
		if gift.PriceType == int(_const.CoinDiamond) {
			if diamondBalance < giftPrint {
				//global2.Log.Infof("宝石余额不够:%d", diamondBalance)
				return &api_models.SendGiftResp{Gold: goldBalance, Diamond: diamondBalance}, errors.New(global2.Lang.T("insufficient_balance"))
			}
			diamondBalance = diamondBalance - giftPrint
		} else if gift.PriceType == int(_const.CoinGold) {
			if goldBalance < giftPrint {
				//global2.Log.Infof("钻石余额不够:%d", goldBalance)
				return &api_models.SendGiftResp{Gold: goldBalance, Diamond: diamondBalance}, errors.New(global2.Lang.T("insufficient_balance"))
			}
			goldBalance = goldBalance - giftPrint
		}
	}
	//fixme gift.price 的价格是不是要使用打折后的价格
	return &api_models.SendGiftResp{
		Gold:      goldBalance,
		Diamond:   diamondBalance,
		GiftPrice: gift.Price,
		PriceType: gift.PriceType,
	}, nil
}

/**
 * @Author
 * @Description // 发送礼物,礼物扣减
 * @Date 9:50 2022/9/28
 * @Param
 * @return
 **/
func SendGiftSubtract(r *api_models.SendGiftReq, uid int) (*api_models.SendGiftResp, error) {
	var gift *models_main.Gift
	var liveInfo *models_live.Live
	var anchor *models_main.User
	//参数条件检测
	//gift *models.Gift, liveInfo *models.Live, anchor *models.User, r *api_models.SendGiftReq, uid int
	if err := SendGiftSubtractCheckParam(&gift, &liveInfo, &anchor, r, uid); err != nil {
		return nil, err
	}
	//操作
	operate, err := SendGiftSubtractOperate(gift, liveInfo, anchor, r, uid)
	//打开redis开关
	//openBlockList()
	return operate, err

}

/**
 * @Author admin
 * @Description //向redis key 中增加一个值 实现BLOCK 队列  在队列中的值 还未有取出之前使其队列中仅有一个值，实现开关功能
 * @Date 17:47 2022/9/28
 * @Param
 * @return
 **/
func openBlockList() (int, error) {
	stringLua := `if (redis.call('llen',KEYS[1])==0) then
   redis.call('lpush',KEYS[1],'')
   return 1
   else
   return 0
 end`
	return global2.GetRds().Eval(stringLua, []string{_const.BLOCK_LIST_GIFT_KEY}).Int()
}

func GetGiftList() ([]*models_main.Gift, error) {
	var gifts []*models_main.Gift
	err := cache.GetCachedData(_const.REDIS_GIFTLIST_CACHE, &gifts)
	if err != nil {
		gifts, err = db_main.GetGiftsList()
		if err != nil {
			return nil, err
		}
		err = cache.SetCacheData(_const.REDIS_GIFTLIST_CACHE, gifts, time.Second*30)
		if err != nil {
			global2.Log.Errorf("cache.SetCacheData err:%v", err)
		}
	}
	/*	configPub := cache.GetConfigPub()
		for _, v := range gifts {
			tmpIcon := strings.Split(v.Icon, "/")
			tmpAnimation := strings.Split(v.Animation, "/")
			if len(tmpAnimation) > 2 {
				v.Animation = fmt.Sprintf("%s/%s/%s", configPub.ImgDomain, tmpAnimation[len(tmpAnimation)-2], tmpAnimation[len(tmpAnimation)-1])
			}
			if len(tmpIcon) > 2 {
				v.Icon = fmt.Sprintf("%s/%s/%s", configPub.ImgDomain, tmpIcon[len(tmpIcon)-2], tmpIcon[len(tmpIcon)-1])
			}

		}*/
	return gifts, nil
}

/**
 * @Author willi
 * @Description //新增加的,暂时未有使用
 * @Date 11:08 2022/9/15
 * @Param
 * @return
 **/
func GetGiftListNew(userId int) ([]*models_main.Gift, error) {
	if userId <= 0 {
		var gifts []*models_main.Gift
		err := cache.GetCachedData(_const.REDIS_GIFTLIST_CACHE, &gifts)
		if err != nil {
			gifts, err = db_main.GetGiftsParamNew(userId)
			if err != nil {
				return nil, err
			}

			err = cache.SetCacheData(_const.REDIS_GIFTLIST_CACHE, gifts, time.Second*60)
			if err != nil {
				global2.Log.Errorf("cache.SetCacheData err:%v", err)
			}
		}
		return gifts, nil
	} else {
		if gifts, err := db_main.GetGiftsParamNew(userId); err != nil {
			return nil, err
		} else {
			return gifts, nil
		}
	}
}

/**
 * @Author
 * @Description //参数条件检测
 * @Date 9:52 2022/9/28
 * @Param
 * @return
 **/
func SendGiftSubtractCheckParam(gift **models_main.Gift, liveInfo **models_live.Live, anchor **models_main.User, r *api_models.SendGiftReq, uid int) (err error) {
	//check liveid 这个需要请求方添加
	/*if int(r.Liveid) == 0 {
		return nil, errors.New("直播间liveid不对")
	}*/
	if r.Anchorid == uid {
		return errors.New("无法给自己赠送礼物")
	}
	if r.Count < 1 { //range
		return errors.New("礼物数量不能小于1")
	}
	if *gift, err = cache.GetGiftInfo(r.Giftid); err != nil {
		return err
	}
	if (*gift).Type == _const.GIFTTYPE_LUXURY && r.Count > 1 {
		global2.Log.Infof("6GIFTTYPE_LUXURY 赠送豪华礼物数量不能大于1:%d 数量:%d", (*gift).Type, r.Count)
		return errors.New("豪华礼物数量不能大于1")
	}
	if !util.ValidRange(_const.VALUE_GIFT_PRICE, float64((*gift).Price)) {
		return errors.New("礼物价格错误")
	}
	*liveInfo, _ = db_live.GetLiveParam(map[string]interface{}{"anchorid": r.Anchorid})
	if (*liveInfo).Liveid == 0 {
		return errors.New("找不到主播的直播间")
	}
	*anchor, err = cache.GetUserFullInfo(r.Anchorid)
	if err != nil {
		return errors.New("无此主播")
	}
	return nil
}

/**
 * @Author
 * @Description //礼物扣减操作
 * @Date 10:25 2022/9/28
 * @Param
 * @return  bool
 **/
func SendGiftSubtractOperate(gift *models_main.Gift, liveInfo *models_live.Live, anchor *models_main.User, r *api_models.SendGiftReq, uid int) (*api_models.SendGiftResp, error) {
	userDiscount := &models_main.TempNoble{}
	var giftLogRecord *models_main.GiftLog
	original := gift.Price * r.Count // 原价
	session := global2.GetX().Begin()
	b := true
	if gift.Type == _const.ActivityGiftType {
		//扣减礼物
		if db_main.UseProps(session, uid, r.Giftid, _const.ActivityGiftType, r.Count) {
			giftLogRecord = CreateGiftLog(r, gift, liveInfo, anchor, userDiscount, uid, original, original)
			b = false
		}
	}
	if b {
		userDiscount = db_main.GetUserDiscount(uid)
		giftPrint := original * userDiscount.GiftDiscount / 100 //拆扣价
		giftLogRecord = CreateGiftLog(r, gift, liveInfo, anchor, userDiscount, uid, original, giftPrint)
		userProfitRecord := CreateUserProfitFromGift(giftLogRecord)
		//更新会员余额与钱包
		if _, _, err := db_main.UpdateAmountByTx(uid, -giftPrint, _const.FundsType(gift.PriceType), session, _const.WALLET_BALANCE_HIDE, []*models_main.UserProfit{userProfitRecord}); err != nil {
			session.Rollback()
			return nil, errors.New(global2.Lang.T("insufficient_balance"))
		}
	}

	cache.DelUserFullCache(uid)

	//记录送礼log
	if _, err := db_main.InsertLogByTx(session, giftLogRecord); err != nil {
		session.Rollback()
		return nil, errors.New("记录送礼log失败!")
	}
	giftLogRecordStr, err := util.Struct2Json(giftLogRecord)
	if err != nil {
		session.Rollback()
		return nil, errors.New("giftLog对象生成字符串失败")
	}

	if session.Commit().Error != nil {
		session.Rollback()
		return nil, errors.New("giftLog事务提交失败!")
	}
	//重新开启线程发送MQ
	go GiftPublishConfirm(global2.GetGiftRabbitMQ(), giftLogRecordStr, giftLogRecord.OperateId, _const.MQ_FAIL_RETRY_TIME)
	//查询余额
	diamondBalance, goldBalance, _ := db_main.GetBalance(uid)
	//
	return &api_models.SendGiftResp{
		Gold:      goldBalance,
		Diamond:   diamondBalance,
		GiftPrice: gift.Price,
		PriceType: gift.PriceType,
	}, nil
}

func GiftPublishConfirm(giftRabbitMQ *global2.RabbitMQ, message, operateId string, retryTime int) {
	channel := giftRabbitMQ.OpenChannel()
	defer channel.Close()
	channel.Confirm(false)
	header := make(map[string]interface{}, 1)
	header["retry_nums"] = 0
	confirms := channel.NotifyPublish(make(chan amqp.Confirmation, 1)) // 处理确认逻辑
	//2.发送消息
	err := channel.Publish(
		giftRabbitMQ.Exchange,
		//要设置
		giftRabbitMQ.Key,
		false,
		false,
		amqp.Publishing{
			ContentType: "text/plain",
			Body:        []byte(message),
			Headers:     header,
		})
	//Headers Table 这个里面可以设置消费次数
	if err != nil {
		fmt.Println(err)
	}
	if confirmed := <-confirms; confirmed.Ack {
		//事务补偿机制 也许MQ_FAIL_RETRY_TIME 也提交失败， 后补轮循线程又会发送一次，  在接受端，根据id 重复来限制重复消费
		if _, err = db_main.UpdateGiftLogByIdConfirm(operateId); err != nil {
			for i := 0; i < _const.MQ_FAIL_RETRY_TIME-1; i++ {
				if _, err = db_main.UpdateGiftLogByIdConfirm(operateId); err == nil {
					break
				}
			}
		}
	} else {
		//事务补偿机制  如retryTime 次后任未发送成功, 后补 轮循线程重新发送
		retryTime--
		if retryTime > 0 {
			GiftPublishConfirm(giftRabbitMQ, message, operateId, retryTime)
		} else {
			//放到REDIS 中,后补 轮循线程重新发送
			global2.GetRds().LPush(_const.BLOCK_LIST_GIFT_KEY, operateId)
		}
	}
}

/**
 * @Author admin
 * @Description //送礼物人交易记录
 * @Date 14:06 2022/9/28
 * @Param
 * @return
 **/
func CreateUserProfitFromGift(gift *models_main.GiftLog) *models_main.UserProfit {
	content := fmt.Sprintf("赠送主播(ID:%d)礼物ID:%dx%d 消费%d%s", gift.Anchorid, gift.Giftid, gift.Count, gift.ActualSpend/100, util.GetCoinName(gift.SpendType))
	return &models_main.UserProfit{
		Uid:         gift.Uid,            //送的人
		FUid:        gift.Anchorid,       //得的人
		CoinCount:   -gift.ActualSpend,   //总价格
		Type:        _const.TYPE_CONSUME, //这是一个消费记录
		ConsumeType: _const.CONSUME_GIFT,
		Content:     content,
		Resid:       gift.Giftid,
		Count:       gift.Count,
		CreateTime:  gift.CreateTime,
		CoinType:    gift.SpendType,
		OrderNo:     util.GenOrderId("GS", 1),
		Liveid:      gift.Liveid,
	}
}

/**
 * @Author admin
 * @Description //TODO
 * @Date 14:30 2022/9/28
 * @Param
 * @return
 **/
func CreateGiftLog(r *api_models.SendGiftReq, gift *models_main.Gift, liveInfo *models_live.Live, anchor *models_main.User, userDiscount *models_main.TempNoble, uid int, original, giftPrint int) *models_main.GiftLog {
	remark := ""
	if gift.Type != _const.ActivityGiftType {
		if len(userDiscount.Name) > 0 {
			remark = fmt.Sprintf("%s%s", userDiscount.Name, global2.Lang.T("discount"))
		}
	} else {

	}
	/*iSharingRatio := anchor.SharingRatio
	if iSharingRatio <= 0 {
		global.Log.Errorf("6SendGift9主播分成设置有错，订单设为最低的1%")
		iSharingRatio = 1
	}*/
	//anchorDiamondGet := giftPrint * iSharingRatio / 100 //假设是鹰钻，那得到的就是这个值

	return &models_main.GiftLog{
		Giftid: r.Giftid, Anchorid: anchor.Id, Uid: uid,
		Liveid: liveInfo.Liveid, Count: r.Count,
		Spend: original, SpendType: gift.PriceType, CreateTime: time.Now(),
		AnchorProfit: 0, ActualSpend: giftPrint, Remark: remark, GiftType: gift.Type,
		OperateId: strings.ReplaceAll(uuid.New().String(), "-", ""), OperateTime: time.Now()}
}
